﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Base;
using UtilZ.Dotnet.Ex.Exceptions;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Base
{
    /// <summary>
    /// tcp客户端
    /// </summary>
    public class NetTcpClient : ClientAbs
    {
        private readonly TcpClientInitPara _tcpClientInitPara;
        /// <summary>
        /// 
        /// </summary>
        protected readonly string _remoteIpStr;

        private NetLinkInfo _linkInfo = null;
        /// <summary>
        /// 获取连接终节点信息
        /// </summary>
        public NetLinkInfo LinkInfo
        {
            get { return _linkInfo; }
        }



        /// <summary>
        /// 连接状态改变通知事件
        /// </summary>
        public event EventHandler<NetConnectedChangedArgs> ConnectChanged;

        /// <summary>
        /// 连接关闭通知
        /// </summary>
        protected void ConnectionCloseNotify()
        {
            var handler = this.ConnectChanged;
            if (handler != null)
            {
                EndPoint remoteEndPoint = this._tcpClientInitPara.RemoteEndPoint;
                EndPoint localEndPoint = null;
                try
                {
                    Socket client = this._client;
                    if (client != null)
                    {
                        localEndPoint = client.LocalEndPoint;
                    }
                }
                catch (ObjectDisposedException)
                {

                }
                catch (Exception ex)
                {
                    ZLoger.Error(ex);
                }

                var args = new NetConnectedChangedArgs(ConnectionStatus.Closed, localEndPoint, remoteEndPoint);
                handler(this, args);
            }

            //重新连接
            this.Reconnect();
        }

        /// <summary>
        /// 连接打开通知
        /// </summary>
        protected void ConnectionOpenNotify()
        {
            var handler = this.ConnectChanged;
            if (handler != null)
            {
                EndPoint remoteEndPoint = this._tcpClientInitPara.RemoteEndPoint;
                EndPoint localEndPoint = null;
                ConnectionStatus status;
                try
                {
                    Socket client = this._client;
                    if (client != null)
                    {
                        localEndPoint = client.LocalEndPoint;
                        if (client.Connected)
                        {
                            status = ConnectionStatus.Opened;
                        }
                        else
                        {
                            status = ConnectionStatus.Closed;
                        }
                    }
                    else
                    {
                        status = ConnectionStatus.Closed;
                    }
                }
                catch (ObjectDisposedException)
                {
                    status = ConnectionStatus.Closed;
                }
                catch (Exception ex)
                {
                    ZLoger.Error(ex);
                    status = ConnectionStatus.Closed;
                }

                var args = new NetConnectedChangedArgs(status, localEndPoint, remoteEndPoint);
                handler(this, args);
            }
        }




        private Socket _client = null;
        /// <summary>
        /// Socket
        /// </summary>
        public Socket Socket
        {
            get
            {
                return _client;
            }
        }

        /// <summary>
        /// 获取socket连接状态
        /// </summary>
        public bool Connected
        {
            get
            {
                var client = this._client;
                if (client == null)
                {
                    return false;
                }
                else
                {
                    return client.Connected;
                }
            }
        }


        private readonly byte[] _buffer;
        private readonly BinaryReader _reader;
        private readonly TcpUnpackerPara _unpackerPara;
        private readonly CancellationTokenSource _cts;
        private int _continuousReceiveEmptyDataCount = 0;



        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="initPara">Tcp客户端初始化参数</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public NetTcpClient(TcpClientInitPara initPara)
            : base(initPara)
        {
            this._tcpClientInitPara = initPara;

            this._remoteIpStr = NetHelper.GetIPString(initPara.RemoteEndPoint);
            this._buffer = new byte[initPara.BufferSize];
            if (initPara.Certificate == null)
            {
                this._reader = new BinaryReader(new MemoryStream(this._buffer));
            }
            else
            {
                //todo 理论代码先写好,ssl不好找机会验证
                var sslStream = new SslStream(new MemoryStream(this._buffer));
                sslStream.AuthenticateAsServer(initPara.Certificate, false, SslProtocols.Tls, true);
                sslStream.ReadTimeout = 10000;
                sslStream.WriteTimeout = 10000;
                this._reader = new BinaryReader(sslStream);
            }
            this._cts = new CancellationTokenSource();
            this._unpackerPara = new TcpUnpackerPara()
            {
                Buffer = this._buffer,
                Client = this,
                Reader = this._reader
            };
            if (initPara.ConnectType == ConnectTypes.Passive)
            {
                this.SetTcpClient(initPara.Client);
            }
        }


        /// <summary>
        /// 同步连接到tcp服务端
        /// </summary>
        public void Connect()
        {
#if NET4_0
            var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
#else
            var client = new Socket(SocketType.Stream, ProtocolType.Tcp);
#endif
            try
            {
                var localEndPoint = this._tcpClientInitPara.LocalEndPoint;
                if (localEndPoint != null)
                {
                    client.Bind(localEndPoint);
                }

                client.Connect(this._tcpClientInitPara.RemoteEndPoint);
                this.SetTcpClient(client);
            }
            catch
            {
                client.Dispose();
                throw;
            }
        }

        /// <summary>
        /// 异步连接到tcp服务端
        /// </summary>
        public void ConnectAsync()
        {
            Task.Factory.StartNew(this.PrimitiveConnectAsync);
        }

        /* .net framwork 不支持这种写法,为了减少代码量,和.NET CORE用一种写法
        /// <summary>
        /// 开启tcp客户端，连接tcp服务器
        /// </summary>
        private async Task PrimitiveConnectAsync()
        {
            //连接tcp服务器
            var initPara = this._tcpClientInitPara;
            int reconnectIntervalMilliseconds = initPara.ReconnectIntervalMilliseconds;
            var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                await client.ConnectAsync(initPara.RemoteEndPoint, CancellationToken.None);
                this.SetTcpClient(client);
                return;
            }
            catch (SocketException sex) when (NetHelper.SocketErrorIsConnectFail(sex.SocketErrorCode))
            {
                //由于目标计算机积极拒绝，无法连接
                if (reconnectIntervalMilliseconds > 0)
                {
                    ZLoger.Warn(sex, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                    Thread.Sleep(reconnectIntervalMilliseconds);
                }
                else
                {
                    ZLoger.Warn(sex, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,立即重试...");
                }
            }
            catch (Exception exi)
            {
                if (reconnectIntervalMilliseconds > 0)
                {
                    ZLoger.Warn(exi, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                    Thread.Sleep(reconnectIntervalMilliseconds);
                }
                else
                {
                    ZLoger.Warn(exi, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,立即重试...");
                }
            }

            client.Dispose();
            await this.PrimitiveConnectAsync();
        }
        */

        /// <summary>
        /// 开启tcp客户端，连接tcp服务器
        /// </summary>
        private void PrimitiveConnectAsync()
        {
            //连接tcp服务器
            var initPara = this._tcpClientInitPara;
            int reconnectIntervalMilliseconds = initPara.ReconnectIntervalMilliseconds;
            try
            {
                var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                //连接tcp服务器
                SocketAsyncEventArgs connectEventArg = new SocketAsyncEventArgs();
                connectEventArg.Completed += ConnectEventArg_Completed;
                connectEventArg.RemoteEndPoint = initPara.RemoteEndPoint;//设置要连接的tcp服务器地址
                connectEventArg.AcceptSocket = client;
                bool willRaiseEvent = client.ConnectAsync(connectEventArg);
                if (!willRaiseEvent)
                {
                    this.SetTcpClient(client);
                }
            }
            catch (SocketException sex) when (NetHelper.SocketErrorIsConnectFail(sex.SocketErrorCode))
            {
                //由于目标计算机积极拒绝，无法连接
                if (reconnectIntervalMilliseconds > 0)
                {
                    ZLoger.Warn(sex, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                    Thread.Sleep(reconnectIntervalMilliseconds);
                }
                else
                {
                    ZLoger.Warn(sex, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,立即重试...");
                }
            }
            catch (Exception exi)
            {
                if (reconnectIntervalMilliseconds > 0)
                {
                    ZLoger.Warn(exi, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,等待{reconnectIntervalMilliseconds}毫秒后重试...");
                    Thread.Sleep(reconnectIntervalMilliseconds);
                }
                else
                {
                    ZLoger.Warn(exi, $"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,立即重试...");
                }
            }
        }

        private void ConnectEventArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                if (e.LastOperation == SocketAsyncOperation.Connect && e.SocketError == SocketError.Success)
                {
                    this.SetTcpClient(e.ConnectSocket);
                }
                else
                {
                    e.AcceptSocket.Dispose();
                    var initPara = this._tcpClientInitPara;
                    int reconnectIntervalMilliseconds = initPara.ReconnectIntervalMilliseconds;
                    //由于目标计算机积极拒绝，无法连接
                    if (reconnectIntervalMilliseconds > 0)
                    {
                        ZLoger.Warn($"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,Socket错误\"{e.SocketError.ToString()}\",等待{reconnectIntervalMilliseconds}毫秒后重试...");
                        Thread.Sleep(reconnectIntervalMilliseconds);
                    }
                    else
                    {
                        ZLoger.Warn($"连接到服务端\"{NetHelper.GetIPPortString(initPara.RemoteEndPoint)}\"失败,Socket错误\"{e.SocketError.ToString()}\",立即重试...");
                    }

                    if (this._disposed)
                    {
                        return;
                    }
                    else
                    {
                        this.ConnectAsync();
                    }
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }

        private void SetTcpClient(Socket client)
        {
            if (this._tcpClientInitPara.KeepAlive)
            {
#if CORE
                client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, this._tcpClientInitPara.KeepAliveTimeSeconds);
                client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, this._tcpClientInitPara.KeepAliveIntervalSeconds);
                client.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, this._tcpClientInitPara.KeepRetryCount);
#else
                int size = System.Runtime.InteropServices.Marshal.SizeOf((uint)0);
                byte[] keepaliveOptionsValues = new byte[size * 3];
                int offset = 0;

                uint keepAliveEnable = 1;
                uint keepAliveTimeMilliseconds = (uint)this._tcpClientInitPara.KeepAliveTimeSeconds * 1000;
                uint keepAliveIntervalMilliseconds = (uint)this._tcpClientInitPara.KeepAliveIntervalSeconds * 1000;

                BitConverter.GetBytes(keepAliveEnable).CopyTo(keepaliveOptionsValues, offset);
                offset += size;

                BitConverter.GetBytes(keepAliveTimeMilliseconds).CopyTo(keepaliveOptionsValues, offset);
                offset += size;

                BitConverter.GetBytes(keepAliveIntervalMilliseconds).CopyTo(keepaliveOptionsValues, offset);

                client.IOControl(IOControlCode.KeepAliveValues, keepaliveOptionsValues, null);
                client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
#endif
            }

            this._linkInfo = new NetLinkInfo(client.RemoteEndPoint, client.LocalEndPoint);
            this._unpackerPara.LinkInfo = this._linkInfo;
            this._client = client;
            this.ConnectionOpenNotify();

            if (this._tcpClientInitPara.SendMillisecondsTimeout > 0)
            {
                client.SendTimeout = this._tcpClientInitPara.SendMillisecondsTimeout;
            }

            if (this._tcpClientInitPara.ReceiveMillisecondsTimeout > 0)
            {
                client.ReceiveTimeout = this._tcpClientInitPara.ReceiveMillisecondsTimeout;
            }

            switch (this._tcpClientInitPara.ReceiveDataMode)
            {
                case ThreadMode.Async:
                    //异步接收数据
                    var recvEventArgs = new SocketAsyncEventArgs();
                    recvEventArgs.AcceptSocket = client;
                    recvEventArgs.SetBuffer(this._buffer, 0, this._buffer.Length);
                    recvEventArgs.Completed += RecvEventArgs_Completed;
                    this.StartRecv(recvEventArgs);
                    break;
                case ThreadMode.Sync:
                    //同步接收数据                    
                    Task.Factory.StartNew(this.SyncRecceiveDataCallback, client, TaskCreationOptions.LongRunning);
                    break;
                case ThreadMode.None:
                default:
                    //不接收数据
                    break;
            }
        }


        private bool SetReaderPosition(BinaryReader reader, long pos)
        {
            try
            {
                if (reader == null)
                {
                    return false;
                }

                Stream stream = reader.BaseStream;
                if (stream == null)
                {
                    return false;
                }

                stream.Position = pos;
                return true;
            }
            catch (ObjectDisposedException)
            {
                return false;
            }
            catch (Exception ex)
            {
                ZLoger.Warn(ex);
                return false;
            }
        }

        private void SyncRecceiveDataCallback(object obj)
        {
            Socket client = (Socket)obj;
            try
            {
                TcpUnpackerPara unpackerPara = this._unpackerPara;
                int preReceiveMaxSize = this._tcpClientInitPara.ReceiveDataSize;
                int preReceiveSize = preReceiveMaxSize, modReceiveSize;
                int revLen, writeOffset = 0, readOffset = 0;
                int totalDataLength = 0, unpackedDataLength;
                byte[] buffer = this._buffer;
                int receiveErrorCount = 0;

                while (!this._disposed)
                {
                    try
                    {
                        revLen = client.Receive(buffer, writeOffset, preReceiveSize, SocketFlags.None);
                        receiveErrorCount = 0;
                        if (revLen <= 0)
                        {
                            this._continuousReceiveEmptyDataCount++;
                            if (this.CheckContinuousReceiveEmptyDataToLimit())
                            {
                                //连接收到长度为0的包,tcp连接意外断开
                                break;
                            }
                            else
                            {
                                //注:即使是接收到数据长度等于0,也还是触发拆包,有些特殊场景下以发送数据长度为0的包来作边界处理,所以不能忽略长度为0的数据处理逻辑
                            }
                        }
                        else
                        {
                            this._continuousReceiveEmptyDataCount = 0;
                            writeOffset += revLen;
                            totalDataLength += revLen;
                        }

                        //分包参数
                        if (!this.SetReaderPosition(unpackerPara.Reader, readOffset))
                        {
                            //资源已释放
                            break;
                        }
                        unpackerPara.Offset = readOffset;
                        unpackerPara.DataLength = totalDataLength;
                        unpackerPara.ReceiveLength = revLen;

                        //分包
                        unpackedDataLength = this._tcpClientInitPara.Unpacker.UnPackge(unpackerPara);
                        if (unpackedDataLength != 0)
                        {
                            readOffset += unpackedDataLength;
                            totalDataLength -= unpackedDataLength;
                        }

                        if (totalDataLength == 0)
                        {
                            //总数据长度为0时,默认从0开始接收
                            writeOffset = 0;
                            readOffset = 0;
                            preReceiveSize = preReceiveMaxSize;
                        }
                        else
                        {
                            if (buffer.Length - writeOffset < preReceiveMaxSize)
                            {
                                //剩余接收数据空间不足,将未解析数据移动到buffer开始位置
                                if (totalDataLength > 0)
                                {
                                    Array.Copy(buffer, readOffset, buffer, 0, totalDataLength);
                                }

                                readOffset = 0;
                                writeOffset = totalDataLength;
                            }

                            //重新计算准备接收数据长度
                            modReceiveSize = buffer.Length - totalDataLength;
                            if (preReceiveSize >= modReceiveSize)
                            {
                                preReceiveSize = modReceiveSize;
                            }
                            else
                            {
                                if (modReceiveSize >= preReceiveMaxSize)
                                {
                                    preReceiveSize = preReceiveMaxSize;
                                }
                                else
                                {
                                    preReceiveSize = modReceiveSize;
                                }
                            }
                        }
                    }
                    catch (ArgumentOutOfRangeException)
                    {
                        totalDataLength = 0;
                        if (!this.SetReaderPosition(unpackerPara.Reader, 0L))
                        {
                            //资源已释放
                            break;
                        }
                        writeOffset = 0;
                    }
                    catch (SocketException sex)
                    {
                        if (sex.SocketErrorCode == SocketError.TimedOut)
                        {
                            //设置了接收数据超时时长,接收数据超时,继续接收
                            continue;
                        }

                        if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                        {
                            ZLoger.Warn(sex, $"同步接收数据失败,错误码:\"{sex.SocketErrorCode.ToString()}\"");
                            break;
                        }
                        else
                        {
                            receiveErrorCount++;
                            if (receiveErrorCount >= this._tcpClientInitPara.SynReceiveErrorMaxRepeatCount)
                            {
                                ZLoger.Error(sex, $"同步接收数据时发生异常,错误码:\"{sex.SocketErrorCode.ToString()}\",超过最大重试次数:\"{this._tcpClientInitPara.SynReceiveErrorMaxRepeatCount}\"，断开连接");
                                break;
                            }
                            else
                            {
                                ZLoger.Error(sex, $"同步接收数据时发生异常,错误码:\"{sex.SocketErrorCode.ToString()}\"");
                                totalDataLength = 0;
                                if (!this.SetReaderPosition(unpackerPara.Reader, 0L))
                                {
                                    //资源已释放
                                    break;
                                }
                                writeOffset = 0;
                                continue;
                            }
                        }
                    }
                    catch (ThreadAbortException)
                    {
                        break;
                    }
                    catch (Exception ex)
                    {
                        ZLoger.Error(ex, "同步接收数据异常");
                    }
                }
            }
            catch (ThreadAbortException)
            { }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }

            try
            {
                client.Dispose();
                this._client = null;

                //发送连接断开通知
                this.ConnectionCloseNotify();
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }

        /// <summary>
        /// 检查连接收到长度为0的数据时否达到上限[达到上限返回true,否则返回false]
        /// </summary>
        /// <returns>达到上限返回true,否则返回false</returns>
        private bool CheckContinuousReceiveEmptyDataToLimit()
        {
            if (this._tcpClientInitPara.MaxReceiveEmpyDataCount > 0 &&
                this._continuousReceiveEmptyDataCount >= this._tcpClientInitPara.MaxReceiveEmpyDataCount)
            {
                //当连续接收到长度为0的数据MaxReceiveEmpyDataCount次,确定对方断开,不再接收数据,由接收数据循环跳出后触发连接断开通知
                return true;
            }
            else
            {
                return false;
            }
        }


        private void RecvEventArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    this.ProcessAsyncReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    //ProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }
        }

        /// <summary>
        /// tcp客户端开始接受tcp服务器发送的数据
        /// </summary>
        private void StartRecv(SocketAsyncEventArgs args)
        {
            if (this._disposed)
            {
                return;
            }

            bool willRaiseEvent = args.AcceptSocket.ReceiveAsync(args);
            if (!willRaiseEvent)
            {
                this.ProcessAsyncReceive(args);
            }
        }

        private int _totalDataLength = 0;
        private int _readOffset = 0;

        /// <summary>
        /// 处理接受到的tcp服务器数据
        /// </summary>
        /// <param name="e"></param>
        private void ProcessAsyncReceive(SocketAsyncEventArgs e)
        {
            try
            {
                switch (e.SocketError)
                {
                    case SocketError.Success:
                        try
                        {
                            if (e.BytesTransferred <= 0)
                            {
                                this._continuousReceiveEmptyDataCount++;
                                if (this.CheckContinuousReceiveEmptyDataToLimit())
                                {
                                    //连接收到长度为0的包,tcp连接意外断开
                                    return;
                                }
                                else
                                {
                                    //注:即使是接收到数据长度等于0,也还是触发拆包,有些特殊场景下以发送数据长度为0的包来作边界处理,所以不能忽略长度为0的数据处理逻辑
                                }
                            }
                            else
                            {
                                this._totalDataLength += e.BytesTransferred;
                                this._continuousReceiveEmptyDataCount = 0;
                            }

                            TcpUnpackerPara unpackerPara = this._unpackerPara;
                            unpackerPara.Offset = this._readOffset;
                            if (!this.SetReaderPosition(unpackerPara.Reader, this._readOffset))
                            {
                                //资源已释放
                                this.ProcessAsyncReceiveDisconnect(e);
                                return;
                            }
                            unpackerPara.DataLength = this._totalDataLength;
                            unpackerPara.ReceiveLength = e.BytesTransferred;

                            int unpackedDataLength = this._tcpClientInitPara.Unpacker.UnPackge(unpackerPara);
                            if (unpackedDataLength > 0)
                            {
                                this._totalDataLength -= unpackedDataLength;
                                this._readOffset += unpackedDataLength;
                            }

                            if (e.BytesTransferred > 0)
                            {
                                //调整接收位置
                                int preReceiveDataSize = this._tcpClientInitPara.ReceiveDataSize;
                                int receiveWriteOffset = e.Offset + e.BytesTransferred;
                                if (e.Buffer.Length - receiveWriteOffset < preReceiveDataSize)
                                {
                                    if (this._totalDataLength > 0)
                                    {
                                        Array.Copy(e.Buffer, this._readOffset, e.Buffer, 0, this._totalDataLength);
                                    }
                                    this._readOffset = 0;
                                    receiveWriteOffset = this._totalDataLength;
                                }
                                e.SetBuffer(receiveWriteOffset, preReceiveDataSize);
                            }
                        }
                        catch (ArgumentOutOfRangeException)
                        {
                            this._totalDataLength = 0;
                            this._readOffset = 0;
                            e.SetBuffer(0, this._tcpClientInitPara.ReceiveDataSize);
                        }
                        catch (ObjectDisposedException)
                        {
                            return;
                        }
                        catch (Exception ex)
                        {
                            ZLoger.Error(ex, $"异步接收数据处理异常,e.Buffer.Length:{e.Buffer.Length},_readOffset:{this._readOffset},_totalDataLength:{this._totalDataLength}");
                            return;
                        }

                        this.StartRecv(e);
                        break;
                    case SocketError.TimedOut://设置了接收数据超时时长,接收数据超时
                        this.StartRecv(e);
                        break;
                    case SocketError.ConnectionReset://对方程序关掉(关非连接断开)
                    case SocketError.Shutdown://连接已断开
                    default:
                        this.ProcessAsyncReceiveDisconnect(e);
                        break;
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }

        private void ProcessAsyncReceiveDisconnect(SocketAsyncEventArgs e)
        {
            e.Completed -= RecvEventArgs_Completed;

            if (e.AcceptSocket != null)
            {
                e.AcceptSocket.Dispose();
            }

            if (e.ConnectSocket != null)
            {
                e.ConnectSocket.Dispose();
            }

            e.Dispose();

            //连接断开通知
            this.ConnectionCloseNotify();
        }

        private void Reconnect()
        {
            if (this._tcpClientInitPara.ConnectType == ConnectTypes.Active)
            {
                //如果是主动连接，则开始重新连接

                if (this._disposed)
                {
                    return;
                }
                else
                {
                    this.ConnectAsync();
                }
            }
        }


        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            try
            {
                if (this.Connected)
                {
                    this._client.Shutdown(SocketShutdown.Both);
                    this._client.Dispose();
                }
            }
            catch (ObjectDisposedException)
            { }
        }




        private Socket GetSendSocket()
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException(this.ToString());
            }

            Socket client = this._client;
            if (client == null || !client.Connected)
            {
                if (this._tcpClientInitPara.ConnectType == ConnectTypes.Active)
                {
                    throw new ConnectException($"未连接到\"{NetHelper.GetIPPortString(this._tcpClientInitPara.RemoteEndPoint)}\"");
                }
                else
                {
                    throw new ConnectException($"客户端\"{NetHelper.GetIPPortString(this._linkInfo.RemoteEndPoint)}\"已断开连接");
                }
            }

            return client;
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="sendDataOptions">发送的数据选项</param>
        protected override void PrimitiveSend(ISendDataOptions sendDataOptions)
        {
            if (sendDataOptions == null ||
                sendDataOptions.Data == null ||
                sendDataOptions.Data.DataType != BufferDataType.File && sendDataOptions.Data.Length == 0)
            {
                return;
            }

            var tcpSendOptions = (TcpSendDataOptions)sendDataOptions;
            BufferData bufferData = tcpSendOptions.Data;

            switch (bufferData.DataType)
            {
                case BufferDataType.Bytes:
                    this.Send(bufferData.Bytes, (int)bufferData.Offset, (int)bufferData.Length, tcpSendOptions.SocketFlags);
                    break;
                case BufferDataType.Stream:
                    this.Send(bufferData.Stream, bufferData.Offset, bufferData.Length, tcpSendOptions.SocketFlags);
                    break;
                case BufferDataType.File:
                    if (bufferData.Offset <= 0 && bufferData.Length < 0)
                    {
                        this.Send(bufferData.FilePath);
                    }
                    else
                    {
                        using (FileStream fs = File.OpenRead(bufferData.FilePath))
                        {
                            bufferData.UpdateFileBufferLength(fs.Length);
                            this.Send(fs, bufferData.Offset, bufferData.Length, tcpSendOptions.SocketFlags);
                        }
                    }
                    break;
#if CORE
                case BufferDataType.Memory:
                    this.Send(bufferData.MemoryBuffer);
                    break;
#endif
                case BufferDataType.BinaryReader:
                    this.Send(bufferData.Reader.BaseStream, bufferData.Offset, bufferData.Length, tcpSendOptions.SocketFlags);
                    break;
                case BufferDataType.IntPtr:
                    if (bufferData.Length > ArrayEx.MaxLength)
                    {
                        throw new ArgumentOutOfRangeException($"udp发送数据长度不能超过{ArrayEx.MaxLength}");
                    }

                    byte[] buffer = new byte[bufferData.Length];
                    Marshal.Copy(bufferData.Ptr + (int)bufferData.Offset, buffer, 0, buffer.Length);
                    this.Send(buffer, 0, buffer.Length, tcpSendOptions.SocketFlags);
                    break;
                default:
                    throw new NotImplementedException($"{nameof(NetTcpClient)}发送数据,未实现的缓存数据类型{bufferData.DataType.ToString()}");
            }
        }




        #region 同步发送
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">数据</param>
        /// <param name="offset">数据中偏移位置,小于等于0表示从0开始</param>
        /// <param name="length">要发送的数据长度,小于等于0表示发送到数据结尾</param>
        /// <param name="socketFlags">SocketFlags</param>
        public void Send(byte[] buffer, int offset = 0, int length = -1, SocketFlags socketFlags = SocketFlags.None)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (offset < 0)
            {
                offset = 0;
            }
            else if (offset >= buffer.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }

            if (length < 0)
            {
                length = buffer.Length - offset;
            }
            else if (offset + length > buffer.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            Socket client = this.GetSendSocket();
            int sendedLength;
            while (length > 0)
            {
                try
                {
                    sendedLength = client.Send(buffer, offset, length, socketFlags);
                    offset += sendedLength;
                    length -= sendedLength;
                }
                catch (ObjectDisposedException)
                {
                    throw;
                }
                catch (SocketException sex)
                {
                    if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                    {
                        this.ConnectionCloseNotify();
                    }

                    throw;
                }
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="stream">Stream</param>
        /// <param name="offset">数据中偏移位置,小于等于0表示从0开始</param>
        /// <param name="length">要发送的数据长度,小于等于0表示发送到数据结尾</param>
        /// <param name="socketFlags">SocketFlags</param>
        public void Send(Stream stream, long offset = 0, long length = -1, SocketFlags socketFlags = SocketFlags.None)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (offset < 0)
            {
                offset = 0;
            }
            else if (offset >= stream.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }

            if (length < 0)
            {
                length = stream.Length - offset;
            }
            else if (offset + length > stream.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            try
            {
                byte[] buffer;
                if (length >= NetHelper.MTU_MAX)
                {
                    buffer = new byte[NetHelper.MTU_MAX];
                }
                else
                {
                    buffer = new byte[length];
                }

                long mod;
                int sendLength = buffer.Length;
                long endPosition = offset + length;
                stream.Seek(offset, SeekOrigin.Begin);
                while (stream.Position < endPosition)
                {
                    mod = endPosition - stream.Position;
                    if (mod < buffer.Length)
                    {
                        sendLength = (int)mod;
                    }

                    stream.Read(buffer, 0, sendLength);
                    this.Send(buffer, 0, sendLength, socketFlags);
                }
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException sex)
            {
                if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                {
                    this.ConnectionCloseNotify();
                }

                throw;
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        public void Send(string filePath)
        {
            if (string.IsNullOrWhiteSpace(filePath))
            {
                throw new ArgumentNullException(nameof(filePath));
            }

            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException("要发送的文件不存在", filePath);
            }

            try
            {
                Socket client = this.GetSendSocket();
                client.SendFile(filePath);
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException sex)
            {
                if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                {
                    this.ConnectionCloseNotify();
                }

                throw;
            }
        }

#if CORE
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">数据</param>
        public void Send(Memory<byte> buffer)
        {
            try
            {
                Socket client = this.GetSendSocket();
                client.Send(buffer.Span);
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException sex)
            {
                if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                {
                    this.ConnectionCloseNotify();
                }

                throw;
            }
        }
#endif

        #endregion



        #region 异步发送

        private SocketAsyncEventArgs _sendSocketAsyncEventArgs = null;
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer">数据</param>
        /// <param name="offset">数据中偏移位置,小于等于0表示从0开始</param>
        /// <param name="length">要发送的数据大小,小于等于0表示发送到数据结尾</param>
        public void SendAsync(byte[] buffer, int offset = 0, int length = -1)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (offset < 0)
            {
                offset = 0;
            }
            else if (offset >= buffer.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }

            if (length < 0)
            {
                length = buffer.Length - offset;
            }

            if (offset + length > buffer.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            try
            {
                Socket client = this.GetSendSocket();
                if (this._sendSocketAsyncEventArgs == null)
                {
                    this._sendSocketAsyncEventArgs = new SocketAsyncEventArgs();
                    this._sendSocketAsyncEventArgs.Completed += SendSocketAsyncEventArgs_Completed;
                }
                this._sendSocketAsyncEventArgs.SetBuffer(buffer, offset, length);
                if (!client.SendAsync(this._sendSocketAsyncEventArgs))
                {
                    this.ProcessSendCompleted(this._sendSocketAsyncEventArgs);
                }
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException sex)
            {
                if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                {
                    this.ConnectionCloseNotify();
                }

                throw;
            }
        }

        private void SendSocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            this.ProcessSendCompleted(e);
        }

        private void ProcessSendCompleted(SocketAsyncEventArgs e)
        {
            if (e.SocketError != SocketError.Success)
            {
                ZLoger.Error($"发送数据到\"{NetHelper.GetIPPortString(this._linkInfo.RemoteEndPoint)}\"失败");
            }
        }


#if CORE
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="filePath">文件路径</param>
        public async void SendFileAsync(string filePath)
        {
            try
            {
                Socket client = this.GetSendSocket();
                await client.SendFileAsync(filePath, this._cts.Token);
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException sex)
            {
                if (NetHelper.SocketErrorIsDisconnect(sex.SocketErrorCode))
                {
                    this.ConnectionCloseNotify();
                }

                throw;
            }
        }
#endif

        #endregion



        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            try
            {
                this._client?.Dispose();
                this._reader.Dispose();
                this._cts.Cancel();
                this._cts.Dispose();
                if (this._sendSocketAsyncEventArgs != null)
                {
                    this._sendSocketAsyncEventArgs.Dispose();
                }

                AcceptSocketArgs socketArgs = this._tcpClientInitPara.SocketArgs;
                if (socketArgs != null)
                {
                    socketArgs.Listener.IncrementConnections();
                }

                //ZLoger.Warn("NetTcpClient.Dispose");
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex, "资源释放异常");
            }
        }
    }
}
